/* 
Scenic Graphics Library
Copyright (C) 2007 Jouni Tulkki

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USApackage scenic;
*/

package scenic;

import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.Iterator;

import scenic.jni.Renderer;

/**
 * This is the base class for all scene graph nodes. Every node
 * with the exception of the root node has a single parent node. 
 * This means that the scene graph is tree-like in structure.
 */
public abstract class SceneNode {
	static final int safetyMargin = 4;
	
	static final int DRAW_SIMPLE = 0;
	static final int DRAW_SURFACE = 1;
	static final int DRAW_SURFACE2X = 2;
	static final int DRAW_SURFACE4X = 3;
	
	private SceneParent visibleParent;
	private ArrayList<SceneParent> otherVisibleParents;
	
	protected SceneNode() {
	}
	
	void addVisibleParent(SceneParent newParent) {
		if(visibleParent == null) {
			visibleParent = newParent;
			show();
		} else {
			if(otherVisibleParents == null)
				otherVisibleParents = new ArrayList<SceneParent>();
			otherVisibleParents.add(newParent);
		}
	}
	
	void removeVisibleParent(SceneParent oldParent) {
		if(visibleParent == oldParent) {
			if(otherVisibleParents != null && !otherVisibleParents.isEmpty()) {
				visibleParent = otherVisibleParents.remove(0);
			} else {
				visibleParent = null;
				hide();
			}
		} else {
			if(otherVisibleParents != null)
				otherVisibleParents.remove(oldParent);
		}
	}
	
	/**
	 * Removes this scene node from its parent.
	 */
/*	public void close() {
		if(parent != null)
			parent.remove(this);
	} */
	
	abstract void draw(DrawContext context, AffineTransform transform, Rectangle visibleArea);
	
	void prepareDraw(DrawContext context, AffineTransform transform) {
	}
	
	abstract Rectangle getBounds(DrawContext context, AffineTransform transform);
	
	protected boolean isVisible() {
		return visibleParent != null;
	}
	
	protected void show() {		
	}
	
	protected void hide() {		
	}
	
	void changed(Rectangle area) {
		if(visibleParent != null)
			visibleParent.childChanged(area);
		if(otherVisibleParents != null) {
			synchronized(otherVisibleParents) {
				Iterator itr = otherVisibleParents.iterator();
			
				while(itr.hasNext()) {
					((SceneParent)itr.next()).childChanged(area);
				}
			}
		}
	}
	
	void changed() {
		changed(null);
	}
	
	int getDrawType(DrawContext context, AffineTransform transform) {
		return Shape.DRAW_SIMPLE;
	}
	
	static int getSurfaceType(int drawType) {
		if(drawType == DRAW_SURFACE2X)
			return Renderer.SURFACE_TYPE_ALPHA2X;
		if(drawType == DRAW_SURFACE4X)
			return Renderer.SURFACE_TYPE_ALPHA4X;
		if(drawType == DRAW_SURFACE)
			return Renderer.SURFACE_TYPE_ALPHA;
		return Renderer.SURFACE_TYPE_COLOR;
	}
	
	static Rectangle addSafetyMargin(Rectangle bounds) {
		return new Rectangle(bounds.x - safetyMargin, bounds.y - safetyMargin, 
				bounds.width + safetyMargin * 2, bounds.height + safetyMargin * 2);
	}
	
	static Rectangle addMargin(Rectangle bounds, int hmargin, int vmargin) {
		return new Rectangle(bounds.x - hmargin, bounds.y - vmargin, 
				bounds.width + hmargin * 2, bounds.height + vmargin * 2);
	}
}
